package de.bokelberg.ant;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Task; 

public class CssFormatAndSortTask extends Task 
{
	
	protected File inFile;
	
	public void setInFile( File value )
	{
		inFile = value;
	}
	
	protected File outFile;
	
	public void setOutFile( File value )
	{
		outFile = value;
	}
	
	protected boolean debug = false;
	
	public void setDebug( boolean value )
	{
		debug = value;
	}
	
    public void execute() 
    {
        if( inFile == null )
        {
        	throw new BuildException("No inFile set.");
        }
        if( debug )
        {
            log("inFile:" + inFile );
        }
        if( outFile == null )
        {
        	throw new BuildException("No outFile set.");
        }
        if( debug )
        {
            log("outFile:" + outFile );
        }
    
        try 
        {
			formatAndSort();
		} 
        catch (IOException e) 
		{
			throw new BuildException("Error reading inFile.", e );
		}
    }
    
    private void formatAndSort() throws IOException
    {
    	String content = readFile( inFile );
    	ArrayList<CssRecord> records = parseCss( content );
    	sortCssRecords( records );
    	
    	FileWriter out = new FileWriter( outFile );
    	writeCssRecords( out, records );
    	out.close();
    }
    
	private String readFile( File inFile ) throws IOException 
	{
		FileInputStream fis = new FileInputStream( inFile );
		int length = fis.available();
		byte b[]= new byte[ length ];
		fis.read( b );
		String result = new String( b );
		return result;
	}
	
	private ArrayList<CssRecord> parseCss( String css )
    {
		ArrayList<CssRecord> result = new ArrayList<CssRecord>();
		ArrayList<String> currentCss = new ArrayList<String>();
		String[] lines = css.split("\r");
		for( int i = 0; i < lines.length; i++ )
		{
			String line = lines[ i ];
			currentCss.add( line );
			if( line.indexOf("}") >= 0 )
			{
				result.add( parseCssRecord( currentCss ));
				currentCss = new ArrayList<String>();
				//if( result.length > 2 ) break;
			}
		}
		return result;
    }

	private CssRecord parseCssRecord( ArrayList<String> currentCssLines ) 
	{
		CssRecord result = new CssRecord();
		result.lines = currentCssLines;
		result.commentLines = findCssRecordCommentLines( currentCssLines );
		result.name = findCssRecordName( currentCssLines );
		result.properties = sortCssRecordProperties( findCssRecordProperties( currentCssLines ));
		//trace("parseCssRecord " + result.name );
		return result;
	}

	private ArrayList<String> findCssRecordCommentLines( ArrayList<String> currentCssLines ) 
	{
    	ArrayList<String> result = new ArrayList<String>();
    	for( int i = 0; i < currentCssLines.size(); i++ )
    	{
    		String line = currentCssLines.get( i ); 
    		if( line.indexOf("{") >= 0 ) return result;
    		result.add( line );
    	}
    	log("Error in findCssRecordCommentLines: Start of record not found.");
    	return result;
	}

	private String findCssRecordName( ArrayList<String> currentCssLines ) 
	{
    	for( int i = 0; i < currentCssLines.size(); i++ )
    	{
    		String line = currentCssLines.get( i );
    		if( line.indexOf("{") >= 0 )
    		{
    			String[] parts = null;
   				parts = line.split("\\{");
    			String name = parts[ 0 ].trim();
    				
    			while( --i >= 0 && name == "" )
    			{
    				name = currentCssLines.get( i ).trim();
    			}
    			if( name == "" )
    			{
    				break;
    			}
    			return name;
    		}
    	}
    	log("Error in findCssRecordName: Name not found.");
    	return null;
	}

	private ArrayList<CssProperty> findCssRecordProperties( ArrayList<String> currentCssLines ) 
	{
    	int i = 0;
    	ArrayList<CssProperty> result = new ArrayList<CssProperty>();
    	while( i < currentCssLines.size() )
    	{
    		String line = currentCssLines.get( i++ );
    		if( line.indexOf("{") >= 0 ) break;
    	}
    	while( i < currentCssLines.size() )
    	{
    		String line = currentCssLines.get( i++ );
    		if( line.indexOf("}") >= 0 ) break;

    		String[] parts = line.split(":");
    		String name = parts[ 0 ].trim();
    		if( name == "" ) continue;
    		
    		String value = trimValue( parts[ 1 ]);
    		if( value == "" ) continue;
    		
    		result.add( new CssProperty( name, value ));
    	}
    	
    	return result;
	}
	
	private ArrayList<CssProperty> sortCssRecordProperties( ArrayList<CssProperty> properties ) 
	{
		Collections.sort( properties );
		return properties;
	}
	
	private ArrayList<CssRecord> sortCssRecords( ArrayList<CssRecord> records ) 
	{
		Collections.sort( records );
		return records;
	}

    private void writeCssRecords( FileWriter out, ArrayList<CssRecord> records ) 
    {
    	for( int i = 0; i < records.size(); i++ )
    	{
    		writeCssRecord( out, records.get( i ));
    		writeNewLine( out );
    	}
	}
    
    private void writeCssRecord( FileWriter out, CssRecord record )
    {
    	writeString( out, record.name + " {");
    	writeCssProperties( out, record.properties );	
    	writeString( out, "}" );
    }

    private void writeCssProperties( FileWriter out, ArrayList<CssProperty> properties )
    {
    	for( int i = 0; i < properties.size(); i++ )
    	{
    		CssProperty property = properties.get( i );
    		writeString( out, "\t" + property.name + " : " + property.value + ";" );
    	}
    }

    private void writeString( FileWriter out, String string ) 
    {
		try 
		{
			out.write( string + "\n");
		} 
		catch (IOException e) 
		{
			throw new BuildException("Error writing outFile.", e);
		}
	}
    
    private void writeNewLine( FileWriter out ) 
    {
		writeString( out, "");
	}    

    private String trimValue( String value )
    {
    	String result = value.trim();
    	if( result.charAt( result.length() - 1 ) == ';' ) result = result.substring( 0, result.length() - 2 );
    	return result;
    }
    
}

class CssRecord implements Comparable<CssRecord>
{
	public ArrayList<String> lines;
	public ArrayList<String> commentLines;
	public String name;
	public ArrayList<CssProperty> properties;
	
	
	public int compareTo( CssRecord other ) 
	{
		String sortName = getSortName();
		String sortNameOther = other.getSortName();
		return sortName.compareTo( sortNameOther );
	}
	
	private String getSortName()
	{
		if( name.charAt( 0 ) == '.' ) return name.toLowerCase();
		if( name.charAt( 0 ) == '@' ) 
		{
			if( name.equals( "@font-face" ))
			{
				return ".." + name + getFontName();
			}
			else
			{
				return ".." + name;
			}
		}
		if( name.equals( "global" )) return  "..." + name;
		return name;
	}
	
	private String getFontName()
	{
		return properties.get( 0 ).value;
	}
}

class CssProperty implements Comparable<CssProperty>
{
	public String name;
	public String value;

    public CssProperty( String name, String value ) 
    {
    	this.name = name;
    	this.value = value;
    }

	public int compareTo( CssProperty other ) 
	{
		return name.compareTo( other.name );
	}
}

